Skip to content

fix(launcher): a supervised stop exits 0 — force lingering streams instead of failing - #277

Merged
vsits-proxy-builder[bot] merged 2 commits into
cnighswonger:mainfrom
Gunther-Schulz:pr/graceful-stop
Jul 31, 2026
Merged

fix(launcher): a supervised stop exits 0 — force lingering streams instead of failing#277
vsits-proxy-builder[bot] merged 2 commits into
cnighswonger:mainfrom
Gunther-Schulz:pr/graceful-stop

Conversation

@Gunther-Schulz

Copy link
Copy Markdown
Contributor

server.close() waits for in-flight requests — and a live Claude Code session always has one (the streaming /v1/messages response). So the graceful path never resolves on its own and the 5s watchdog is the normal exit under a supervisor, not the exception. Exiting 1 there means every systemctl stop:

  • logs status=1/FAILURE, making a crash and a clean stop indistinguishable in the journal, and
  • trips Restart=on-failure on a deliberate stop.

The watchdog now force-closes lingering connections (closeAllConnections, Node ≥18.2; on the 18.0/18.1 floor the prior behavior is the only option), reports the forcing on stderr, and exits 0. Observed across two days of restarts under systemd on our machines: every stop previously logged FAILURE; with this patch, clean status=0.

Independent of the #272#276 series (found while operating them, touches only the shutdown path).

🤖 Generated with Claude Code

Gunther-Schulz and others added 2 commits July 29, 2026 12:20
…stead of failing

server.close() waits for in-flight requests, and a live Claude Code
session always has one (the streaming /v1/messages response), so the
graceful path alone never resolves — the 5s watchdog is the NORMAL
exit under systemd, not the exception. Exiting 1 there made every
`systemctl stop` log "status=1/FAILURE", which makes a crash and a
clean stop indistinguishable in the journal and trips
Restart=on-failure on a deliberate stop.

The watchdog now force-closes lingering connections
(closeAllConnections, Node >=18.2; older engines keep the prior
behavior), reports the forcing on stderr, and exits 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e stream exits 0

Spawns the real server, opens a request that never completes, sends
SIGTERM, and asserts exit code 0 within the watchdog window. Against
the pre-fix launcher this hangs on close and exits 1 — the exact
journal FAILURE the fix removes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@vsits-codex-review-agent vsits-codex-review-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #277 graceful supervised stop

Date: 2026-07-31
Reviewed: PR #277 at 94363a1a7aaad43f2f28f56c1af1c4bdb7f73ca7
Round: 1
Label applied: approved-by-codex-agent

What Is Correct

  • The shutdown premise is correct: proxy/server.mjs routes CLI shutdown through active.close(), which delegates to server.close(), and Node keeps connections open while they are still sending a request or waiting for a response. That covers both an unfinished request body and the live /v1/messages SSE response.
  • Calling closeAllConnections() only after server.close() is the documented safe ordering.
  • The repo currently declares Node >=18, documents Node.js 18+, and tests majors 18, 20, and 22. On 18.0/18.1 the feature-detect branch is reachable, but the supervisor-facing fix still lands because the watchdog logs and exits 0; only the explicit pre-exit reap is unavailable there.
  • Exiting 0 on a supervised stop is the right contract. A deliberate SIGTERM should not look like a crash to systemd, and stderr preserves visibility when the stop required a forced close.
  • The handleMessages() abort path remains coherent: forced connection teardown will either fire clientReq close and abort upstream, or the immediate process exit will tear the upstream down with the process.
  • test/shutdown-exit-code.test.mjs meaningfully exercises the watchdog path and locks the externally visible contract: delayed stop, stderr notice, and exit code 0.

Blockers

None.

What Needs Attention

  • The new regression test uses an unfinished request body rather than a live SSE response. That still proves the watchdog path and the server.close() behavior this fix depends on, but it leaves the exact mid-stream response shape covered by reasoning rather than by a dedicated test.

Bloat / Non-Functional

None.

Recommendations

  • If this area changes again, keep the test contract black-box and supervisor-facing.
  • If maintainers later need a machine-readable distinction between "drained" and "forced after 5s", add telemetry rather than reusing process exit codes.

Bottom Line

This fixes a real supervisor contract bug without disturbing the embeddable API or the request pipeline. The reasoning is sound, the fallback behavior is acceptable on the repo's declared Node floor, and the regression test locks the intended stop semantics in place. Approved. — Codex review

@vsits-codex-review-agent vsits-codex-review-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review: re-submitting approval after the fork branch was reverted to remove my docs-only review artifact commit.

I previously reviewed and approved this PR at 94363a16. I confirmed the current head c814fd2f differs only by removal of docs/code-reviews/pr-277-round-1-codex.md; there is no code change.

@vsits-codex-review-agent vsits-codex-review-agent Bot added reviewed-by-codex-agent Directive/spec reviewed by Codex — no blocking findings approved-by-codex-agent Final implementation approval from Codex Agent and removed reviewed-by-codex-agent Directive/spec reviewed by Codex — no blocking findings approved-by-codex-agent Final implementation approval from Codex Agent labels Jul 31, 2026
@vsits-proxy-builder
vsits-proxy-builder Bot merged commit 5a9dac0 into cnighswonger:main Jul 31, 2026
2 checks passed
vsits-proxy-builder Bot added a commit that referenced this pull request Jul 31, 2026
…286)

The artifact rule said "PR review -> on the PR branch" without qualifying who owns it, so reviewing a community PR meant pushing to the contributor's branch. That happened on #274 and #277 this week; both were reverted.

maintainerCanModify makes the push possible; it is not permission. It also backfires mechanically: this repo dismisses stale reviews on push, so the review-doc commit dismisses the approval it documents and staleness-flags the approval label against the new head.

Fork PRs now have no committed artifact — the formal review body is the artifact.

Closes #286
cnighswonger pushed a commit that referenced this pull request Jul 31, 2026
The global reviewer bar is "larger than the directive's requirements
justify." Community PRs have no directive, so the bar had nothing to
anchor to: across eight open community PRs every review reported
"Bloat: None", including one on 6,630 lines of new production code.

AGENTS.md now anchors no-directive PRs to the defect being fixed, and
requires the size numbers be stated in every review — a number is
checkable, "None" is not. Calibrated against #274/#277/#261, all merged
and all proportionate, so the reference is real work rather than a
guess. Explicitly excludes test volume and why-comments from the
finding, since both are high here by design.

CONTRIBUTING.md is new: AGENTS.md and CLAUDE.md were already committed
but are addressed to our own agents, so contributors had no file that
spoke to them. Points them at both, and asks PRs over ~300 production
LOC to carry the non-functional checklist.


Claude-Session: https://claude.ai/code/session_01GvZKP1JeXgHFCovTaAPT5B

Co-authored-by: vsits-proxy-builder[bot] <223447982+vsits-proxy-builder[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved-by-codex-agent Final implementation approval from Codex Agent reviewed-by-codex-agent Directive/spec reviewed by Codex — no blocking findings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant